﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

using Sampler;
using Shapes.Geometry;
using Shapes.Misc;
using Shapes.Misc.Appearance;

namespace TestGame.Samples
{
    /// <summary>
    /// This Sample shows, how to move along the outline of a shape.
    /// this could be useful for games where NPCs are walking along a fixed path 
    /// (or not fixed... because you are able to change the shapes at runtime).
    /// </summary>
    public class PathSample
#if XNA4
        : PhoneSample
#else
        : ZuneSample
#endif
    {
        const float Velocity = 30.0f; // pixels per second

        // the generator used to create a visible shape
        TextureGenerator _Generator;

        // the shapes used for the paths
        Shape[] _Paths = new Shape[4]; // works also with drawings

        // a little object following the outlin of the shape
        Shape _Object;

        // the Textures to make the shapes visible (we don't use GeometrySprites just to show how it works without them).
        Texture2D _PathTex, _ObjectTex;

#if !ZUNE
        // the Brush for the path shape.
        Brush _PathBrush;
#endif
        // the Path-Position, which is a value between 0.0f and 1.0f.
        float _CurrentValue;

        // the index of the current path-shape
        int _PathIndex = -1;

        public PathSample(TextureGenerator generator)
            : base(
#if XNA4
            false,
#endif
            "Path Sample:\n\n"
            + "Movement along the outline of \n a shape or drawing.\n"
            + "Press to the right or left \n to change the shape")
        {
            _Generator = generator;
        }



        protected override void Initialize()
        {
            // create some different shapes...
            // a rectangle...
            _Paths[0] = new Rect(100, 150);
            // a triangle
            _Paths[1] = new Triangle(new Vector2(0, 0), new Vector2(100, 50), new Vector2(150, 175));
            // an ellipse... note: the ellipse has a non-consistent path. 
            // means: the path is "longer" at the jolted sides than at the flat ones.
            // in other words: if you split the ellipse into 4 parts (by two separation lines crossing at the center)
            // the path has the same length (length of 0.25f) for every of the quads.
            _Paths[2] = new Ellipse(75, 50);
            // a polygon
            _Paths[3] = new Polygon(GeometryTemplates.CreateKochSnowflake(70, 2, 0));

            // set the shapes position to the center and the rotation point to the center of itself
            foreach (Shape d in _Paths)
            {
                d.Origin = new Vector2(d.Width / 2, d.Height / 2);
                d.Position = new Vector2(120, 160);
            }

            // create a little Circle and set the origin origin to the center
            _Object = new Triangle(new Vector2(0, 0), new Vector2(12, 3), new Vector2(0, 6));
            _Object.Origin = new Vector2(6, 3);

#if !ZUNE // there is no outline on the zune, because it would take very long to create it there
            _PathBrush = Brush.CreateFallOffBrush(25, 20);
            // _PathBrush.AddMask(new BrushMaskStrangeEffect(1.5f, 3)); // try this, if you want...
            _PathBrush.AddMask(new BrushMaskExclusion(true));
            _PathBrush.Color = Color.DarkRed;
#endif
            // make a texture from the little object
            _ObjectTex = _Generator.GeometryFilled(_Object);
            
            // set up the path shape
            SetNextPath();
        }

        // increases the path index by 1 and creates a texture for the shape with the calculated index
        private void SetNextPath()
        {
            _PathIndex = (_PathIndex + 1) % _Paths.Length;
            SetTexture();
        }

        // decreases the path index by 1 and creates a texture for the shape with the calculated index
        private void SetPreviousPath()
        {
            _PathIndex = (_PathIndex - 1 + _Paths.Length) % _Paths.Length;
            SetTexture();
        }

        // Sets the Texture for the current shape
        private void SetTexture()
        {
#if ZUNE    // Filled is much faster than Border. so we only fill the shape at the zune
            _PathTex = _Generator.GeometryFilled(_Paths[_PathIndex], Color.DarkRed);
#else
            _PathTex = _Generator.GeometryFilledWithBorder(_Paths[_PathIndex], Color.Red, _PathBrush);
#endif
        }

        public override void Update(float seconds,
                                GamePadState currentButtons, GamePadState previousButtons,
                                KeyboardState currentKeys, KeyboardState previousKeys,
                                MouseState currentMouse, MouseState previousMouse)
        {
            // calculate the current path value: 
            // (Velocity * seconds) is the distance to go in pixels
            // but the path works with values from 0.0f to 1.0f 
            // so we have to divide the distance by the length of the shapes outline.
            _CurrentValue +=  (Velocity * seconds) / _Paths[_PathIndex].BorderLength;

            // let's rotate the shape to show how well the transformation works :)
            _Paths[_PathIndex].Rotation += 0.25f * seconds;

            // now set the position of the Object to the calculated path value
            _Object.Position = _Paths[_PathIndex].GetPositionFromEdgePath(_CurrentValue);

            // and now we will turn the object to look always forwards
            _Object.Rotation = Angle.CreateFromVector(_Paths[_PathIndex].GetTangent(_Object.Position));

            // check input for next / previous path shape
            if ((currentKeys.IsKeyDown(Keys.Right) && previousKeys.IsKeyUp(Keys.Right))
                || (currentButtons.DPad.Right == ButtonState.Pressed && previousButtons.DPad.Right == ButtonState.Released))
            {
                SetNextPath();
            }
            if ((currentKeys.IsKeyDown(Keys.Left) && previousKeys.IsKeyUp(Keys.Left))
                || (currentButtons.DPad.Left == ButtonState.Pressed && previousButtons.DPad.Left == ButtonState.Released))
            {
                SetPreviousPath();
            }
        }


        // Draws the current path shape and the moving object to the screen.
        public override void Draw(SpriteBatch batch)
        {
            Game.GraphicsDevice.Clear(Color.DarkGreen);

            batch.Begin();

            batch.Draw(_PathTex, _Paths[_PathIndex].Position, null, Color.White, _Paths[_PathIndex].Rotation.ClockwiseRadians, _Paths[_PathIndex].Origin, _Paths[_PathIndex].Scale, SpriteEffects.None, 1);
            batch.Draw(_ObjectTex, _Object.Position, null, Color.Yellow, _Object.Rotation.ClockwiseRadians, _Object.Origin, _Object.Scale, SpriteEffects.None, 0);

            batch.End();
        }

    }
}
